Skip to content

Multi Pointers Technique

Overview

The Multi Pointers technique (also known as Two Pointers) is a powerful algorithmic approach that uses multiple pointers to traverse data structures efficiently. This technique is particularly effective for problems involving arrays, strings, and linked lists.

Core Concepts

What are Multi Pointers?

Multi pointers involve using two or more pointers that move through a data structure according to certain conditions, often reducing time complexity from O(n²) to O(n).

Common Pointer Movements

  1. Opposite Direction: Start from both ends, move toward center
  2. Same Direction: Both pointers move in the same direction at different speeds
  3. Fixed Distance: Maintain constant distance between pointers
  4. Conditional Movement: Move pointers based on specific conditions

Pattern Categories

1. Two Sum Pattern (Opposite Direction)

Use Case: Find pairs that meet certain criteria in sorted arrays

typescript
function twoSum(nums: number[], target: number): number[] {
    let left = 0;
    let right = nums.length - 1;
    
    while (left < right) {
        const sum = nums[left] + nums[right];
        
        if (sum === target) {
            return [left, right];
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    
    return [-1, -1];
}

2. Fast and Slow Pointers (Floyd's Algorithm)

Use Case: Detect cycles, find middle elements

typescript
class ListNode {
    val: number;
    next: ListNode | null;
    constructor(val?: number, next?: ListNode | null) {
        this.val = (val === undefined ? 0 : val);
        this.next = (next === undefined ? null : next);
    }
}

function hasCycle(head: ListNode | null): boolean {
    if (!head || !head.next) return false;
    
    let slow = head;
    let fast = head;
    
    while (fast && fast.next) {
        slow = slow.next!;
        fast = fast.next.next;
        
        if (slow === fast) {
            return true;
        }
    }
    
    return false;
}

function findMiddle(head: ListNode | null): ListNode | null {
    if (!head) return null;
    
    let slow = head;
    let fast = head;
    
    while (fast.next && fast.next.next) {
        slow = slow.next!;
        fast = fast.next.next;
    }
    
    return slow;
}

3. Sliding Window with Pointers

Use Case: Variable-size windows, substring problems

typescript
function lengthOfLongestSubstring(s: string): number {
    const seen = new Set<string>();
    let left = 0;
    let maxLength = 0;
    
    for (let right = 0; right < s.length; right++) {
        while (seen.has(s[right])) {
            seen.delete(s[left]);
            left++;
        }
        
        seen.add(s[right]);
        maxLength = Math.max(maxLength, right - left + 1);
    }
    
    return maxLength;
}

4. Three Pointers

Use Case: Three sum problems, partitioning

typescript
function threeSum(nums: number[]): number[][] {
    nums.sort((a, b) => a - b);
    const result: number[][] = [];
    
    for (let i = 0; i < nums.length - 2; i++) {
        // Skip duplicates for first pointer
        if (i > 0 && nums[i] === nums[i - 1]) continue;
        
        let left = i + 1;
        let right = nums.length - 1;
        
        while (left < right) {
            const sum = nums[i] + nums[left] + nums[right];
            
            if (sum === 0) {
                result.push([nums[i], nums[left], nums[right]]);
                
                // Skip duplicates
                while (left < right && nums[left] === nums[left + 1]) left++;
                while (left < right && nums[right] === nums[right - 1]) right--;
                
                left++;
                right--;
            } else if (sum < 0) {
                left++;
            } else {
                right--;
            }
        }
    }
    
    return result;
}

5. Remove Duplicates Pattern

Use Case: In-place array modification

typescript
function removeDuplicates(nums: number[]): number {
    if (nums.length <= 1) return nums.length;
    
    let writeIndex = 1;
    
    for (let readIndex = 1; readIndex < nums.length; readIndex++) {
        if (nums[readIndex] !== nums[readIndex - 1]) {
            nums[writeIndex] = nums[readIndex];
            writeIndex++;
        }
    }
    
    return writeIndex;
}

Advanced Patterns

1. Palindrome Verification

typescript
function isPalindrome(s: string): boolean {
    let left = 0;
    let right = s.length - 1;
    
    while (left < right) {
        // Skip non-alphanumeric characters
        while (left < right && !isAlphanumeric(s[left])) {
            left++;
        }
        while (left < right && !isAlphanumeric(s[right])) {
            right--;
        }
        
        if (s[left].toLowerCase() !== s[right].toLowerCase()) {
            return false;
        }
        
        left++;
        right--;
    }
    
    return true;
}

function isAlphanumeric(char: string): boolean {
    return /[a-zA-Z0-9]/.test(char);
}

2. Container With Most Water

typescript
function maxArea(height: number[]): number {
    let left = 0;
    let right = height.length - 1;
    let maxWater = 0;
    
    while (left < right) {
        const width = right - left;
        const currentHeight = Math.min(height[left], height[right]);
        const currentArea = width * currentHeight;
        
        maxWater = Math.max(maxWater, currentArea);
        
        // Move the pointer with smaller height
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    
    return maxWater;
}

3. Merge Sorted Arrays

typescript
function merge(nums1: number[], m: number, nums2: number[], n: number): void {
    let i = m - 1;  // Last element in nums1
    let j = n - 1;  // Last element in nums2
    let k = m + n - 1;  // Last position in merged array
    
    while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]) {
            nums1[k] = nums1[i];
            i--;
        } else {
            nums1[k] = nums2[j];
            j--;
        }
        k--;
    }
    
    // Copy remaining elements from nums2
    while (j >= 0) {
        nums1[k] = nums2[j];
        j--;
        k--;
    }
}

When to Use Multi Pointers

Ideal Scenarios

  1. Sorted Arrays: Two sum, three sum problems
  2. Palindromes: String or array palindrome checks
  3. Linked Lists: Cycle detection, finding middle
  4. Array Partitioning: Dutch flag problem
  5. Merging: Combining sorted sequences
  6. Sliding Windows: Variable-size window problems

Problem Indicators

  • Need to find pairs/triplets with specific properties
  • Working with sorted data
  • Need to detect patterns or cycles
  • In-place array modifications required
  • Optimizing from O(n²) to O(n)

Interview Tips

Common Mistakes

  1. Boundary Conditions: Always check pointer bounds
  2. Infinite Loops: Ensure pointers always move
  3. Duplicate Handling: Skip duplicates when necessary
  4. Pointer Initialization: Start pointers at correct positions

Optimization Strategies

  1. Early Termination: Break when conditions are met
  2. Skip Duplicates: Avoid redundant comparisons
  3. Choose Right Pattern: Match technique to problem type
  4. Space Efficiency: Often achieves O(1) space complexity

Problem-Solving Steps

  1. Identify Pattern: Determine which multi-pointer technique applies
  2. Initialize Pointers: Set starting positions correctly
  3. Define Movement Rules: How and when to move each pointer
  4. Handle Edge Cases: Empty arrays, single elements, etc.
  5. Optimize: Look for early termination opportunities

Problem Implementations

This directory contains the following problem solutions:

TypeScript Solutions

Practice Problems

Easy

  • Two Sum II (sorted array)
  • Valid Palindrome
  • Remove Duplicates from Sorted Array
  • Merge Sorted Array
  • Linked List Cycle

Medium

  • 3Sum
  • Container With Most Water
  • Remove Nth Node From End of List
  • Sort Colors (Dutch Flag)
  • Longest Substring Without Repeating Characters
  • Find the Duplicate Number

Hard

  • Trapping Rain Water
  • 4Sum
  • Minimum Window Substring
  • Sliding Window Maximum
  • Longest Substring with At Most K Distinct Characters

Time and Space Complexity

PatternTimeSpaceUse Case
Two Sum (sorted)O(n)O(1)Find pairs
Fast/Slow PointersO(n)O(1)Cycle detection
Three PointersO(n²)O(1)Triplet problems
Sliding WindowO(n)O(k)Substring problems
Array PartitioningO(n)O(1)In-place sorting
  • Sliding Window: Often combined with multi-pointers
  • Binary Search: Can use two pointers for range searches
  • Divide and Conquer: Merge operations use multi-pointers
  • Dynamic Programming: Some DP problems benefit from pointer techniques

Software Engineer Interview Preparation